package com.example.demo.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Scanner;

/*****************************************************************
 *
 * AES加密解密
 *
 * @author x12972
 * @date 2018年5月8日上午10:13:22
 * @since 最早从哪个版本开始，如v1.0.1
 * 
 ****************************************************************/
public class CipherUtils {

    private static final Logger logger = LoggerFactory.getLogger(CipherUtils.class);

    private CipherUtils() {}

    /**
     * 加密-不限制初始化私钥字符串的字节数，会根据输入字符串生成符合位数的私钥
     * 
     * @param encodeRules 加密规则，即生成私钥的初始化内容
     * @param content 待加密的内容
     * @return 经过base64编码的加密内容
     */
	public static String aesEncode(String encodeRules, String content) {
        try {
            // 1.构造密钥生成器，指定为AES算法,不区分大小写
            KeyGenerator keygen = KeyGenerator.getInstance("AES");
            // 2.根据ecnodeRules规则初始化密钥生成器
            // 生成一个128位的随机源,根据传入的字节数组
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            random.setSeed(encodeRules.getBytes());
            keygen.init(128, random);
            // 3.产生原始对称密钥
            SecretKey originalKey = keygen.generateKey();
            // 4.获得原始对称密钥的字节数组
            byte[] raw = originalKey.getEncoded();
            // 5.根据字节数组生成AES密钥
            SecretKey key = new SecretKeySpec(raw, "AES");

            // 使用CBC分割字符方式
            // IvParameterSpec ivSpec = new IvParameterSpec("xuechaooxuechaoo".getBytes());

            // 6.根据指定算法AES自成密码器，默认使用ECB
            Cipher cipher = Cipher.getInstance("AES");
            // 7.初始化密码器，第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作，第二个参数为使用的KEY
            cipher.init(Cipher.ENCRYPT_MODE, key);
            // 8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
            byte[] byteEncode = content.getBytes("utf-8");
            // 9.根据密码器的初始化方式--加密：将数据加密
            byte[] byteAes = cipher.doFinal(byteEncode);
            // 10.将加密后的数据转换为字符串
            // 这里用Base64Encoder中会找不到包
            // 解决办法：
            // 在项目的Build path中先移除JRE System Library，再添加库JRE System Library，重新编译后就一切正常了。
            String aesEncode = new String(Base64.getEncoder().encode(byteAes));
            // 11.将字符串返回
			return aesEncode;
        } catch (NoSuchAlgorithmException e) {
            logger.error("加密错误！");
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            logger.error("加密错误！");
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            logger.error("加密错误！");
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            logger.error("加密错误！");
            e.printStackTrace();
        } catch (BadPaddingException e) {
            logger.error("加密错误！");
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            logger.error("加密错误！");
            e.printStackTrace();
        }
        // 如果有错就返加nulll
        return null;
    }

    /**
     * 解密-不限制初始化私钥字符串的字节数，会根据输入字符串生成符合位数的私钥
     * 
     * @param encodeRules 解密规则，即生成私钥的初始化内容，需与加密规则一致
     * @param content 待解密的内容
     * @return 经过解码并解密的原文
     */
	public static String aesDncode(String encodeRules, String content) {
        try {
            // 1.构造密钥生成器，指定为AES算法,不区分大小写
            KeyGenerator keygen = KeyGenerator.getInstance("AES");
            // 2.根据ecnodeRules规则初始化密钥生成器
            // 生成一个128位的随机源,根据传入的字节数组
//            keygen.init(128, new SecureRandom(encodeRules.getBytes()));
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            random.setSeed(encodeRules.getBytes());
            keygen.init(128, random);
            // 3.产生原始对称密钥
            SecretKey originalKey = keygen.generateKey();
            // 4.获得原始对称密钥的字节数组
            byte[] raw = originalKey.getEncoded();
            // 5.根据字节数组生成AES密钥
            SecretKey key = new SecretKeySpec(raw, "AES");

            // 使用CBC分割字符方式
            // IvParameterSpec ivSpec = new IvParameterSpec("xuechaooxuechaoo".getBytes());

            // 6.根据指定算法AES自成密码器，默认使用ECB
            Cipher cipher = Cipher.getInstance("AES");
            // 7.初始化密码器，第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作，第二个参数为使用的KEY
            cipher.init(Cipher.DECRYPT_MODE, key);
            // 8.将加密并编码后的内容解码成字节数组
            byte[] byteContent = Base64.getDecoder().decode(content);
            /*
             * 解密
             */
            byte[] byteDecode = cipher.doFinal(byteContent);
			String aesDecode = new String(byteDecode, "utf-8");
			return aesDecode;
        } catch (NoSuchAlgorithmException e) {
            logger.error("解密出错！");
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            logger.error("解密出错！");
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            logger.error("解密出错！");
            e.printStackTrace();
        } catch (IOException e) {
            logger.error("解密出错！");
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            logger.error("解密出错！");
            e.printStackTrace();
        } catch (BadPaddingException e) {
            logger.error("解密出错！");
            e.printStackTrace();
        }

        // 如果有错就返加nulll
        return null;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("---------请输入您需要的执行的操作: 模拟登录-login; 加密: com.example.demo.utils.encode; 解密: decode---------");
        String operation = scanner.next();
        switch (operation) {
            case "login":
                // 生成用户名加密
                System.out.println("---------请输入相应域账号---------");
                String loginName = scanner.next();
                String name = CipherUtils.aesEncode("juOp*k24", loginName);
                try {
                    name = new String(Base64.getEncoder().encode(URLEncoder.encode(name, "UTF-8").getBytes()), "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                System.out.println(name);
                break;
            case "com/example/demo/utils/encode":
                System.out.println("---------请输入相应已经加密过的字符串---------");
                String enContent = scanner.next();
                System.out.println("---------请输入相应解密规则,项目大部分使用juOp*k24---------");
                String enRule = scanner.next();
                // 主机密码解密
                System.out.println(CipherUtils.aesDncode(enRule, enContent));
                break;
            case "decode":
                System.out.println("---------请输入需要加密的字符串---------");
                String deContent = scanner.next();
                System.out.println("---------请输入相应加密规则,项目大部分使用juOp*k24---------");
                String deRule = scanner.next();
                // 主机密码解密
                System.out.println(CipherUtils.aesDncode(deRule, deContent));
                break;
            default:
                break;
        }
    }

}
